#include <mem/mem_type.h>
#include <boot/boot.h>
#include <mem/page.h>
#include <str.h>
#include <asm/gdt.h>
#include <printk.h>

struct mem_map __mem_map;

void mem_page_init()
{
}

int mem_init()
{
    i32_t i;
    __mem_map.size = *(u32_t *)(KERNEL_BASE + (MEMORY_INFO_DS << 4) + MEMORY_INFO_COUNT);
    struct mem_type *src = (struct mem_type *)(KERNEL_BASE + (MEMORY_INFO_DS << 4) + MEMORY_INFO_ADDR);

    if (__mem_map.size >= MAX_MEM_SIZE)
    {
        return -1;
    }
    printk("<<<Display memory information.>>>\nType: 1: %s, 2: %s, 3: %s, 4: %s, Others:%s\n",
           "RAM", "ROM or Reserved", "ACPI Reclaim Memory", "ACPI NVS Memory", "Unkonwn");
    for (i = 0; i < __mem_map.size; i++)
    {
        memcpy(src + i, __mem_map.mem + i, sizeof(struct mem_type));
        printk("Addr: %016lx, Length: %016ld, Type: %d\n",
               __mem_map.mem[i].addr, __mem_map.mem[i].len, __mem_map.mem[i].type);
    }
    printk(">>>Display memory information.<<<\n");
    return 0;
}

u64_t to_phy_addr(u64_t addr)
{
    u64_t *pml4_base;
    u64_t *pdpt_entry;
    u64_t *pdt_entry;
    u64_t *pt_entry;
    u64_t tmp;

    __asm__ __volatile__("movq %%cr3, %0" : "=a"(pml4_base));

    tmp = pml4_base[(addr >> 39) & 0x1ffUL]; // pml4-entry
    if (!(tmp & PAGE_PRESENT_BIT))
    {
        return 0;
    }

    pdpt_entry = (u64_t *)(tmp & ~0xfffUL); // pdpt_entry addr
    tmp = pdpt_entry[(addr >> 30) & 0x1ffUL];
    if (!(tmp & PAGE_PRESENT_BIT))
    {
        return 0;
    }
    if (tmp & PAGE_HUGE_BIT)
    {
        return (tmp & ~0x3fffffffUL) + (addr & 0x3fffffffUL);
    }

    pdt_entry = (u64_t *)(tmp & ~0xfffUL);
    tmp = pdt_entry[(addr >> 21) & 0x1ffUL];
    if (!(tmp & PAGE_PRESENT_BIT))
    {
        return 0;
    }
    if (tmp & PAGE_HUGE_BIT)
    {
        return (tmp & ~0x1fffffUL) + (addr & 0x1fffffUL);
    }

    pt_entry = (u64_t *)(tmp & ~0xfffUL);
    tmp = pt_entry[(addr >> 12) & 0x1ffUL];
    if (tmp & PAGE_PRESENT_BIT)
    {
        return (tmp & ~0xfffUL) + (addr & 0xfffUL);
        ;
    }

    return 0;
}